﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;
using DSS_XNA.Engine.Error;
using DSS_XNA.Engine.World;
using DSS_XNA.Engine.World.Worlds.RPGWorld;

namespace TileMapEditor
{
    public partial class frmRPGMapEditor : Form
    {
        #region Properties

        public Bitmap Map
        {
            get { return _map; }
            set { _map = value; }
        }
        private Bitmap _map = null;

        public int WidthInTiles
        {
            get { return _widthInTiles; }
            set { _widthInTiles = value; }
        }
        private int _widthInTiles = 10;

        public int HeightInTiles
        {
            get { return _heightInTiles; }
            set { _heightInTiles = value; }
        }
        private int _heightInTiles = 10;

        public int OffsetX
        {
            get { return _offsetX; }
            set { _offsetX = value; }
        }
        private int _offsetX = 0;

        public int OffsetY
        {
            get { return _offsetY; }
            set { _offsetY = value; }
        }
        private int _offsetY = 0;

        public int MouseOverTileIdx
        {
            get { return _mouseOverTileIdx; }
            set { _mouseOverTileIdx = value; }
        }
        private int _mouseOverTileIdx = 0;

        public int TileOverX
        {
            get { return _tileOverX; }
            set { _tileOverX = value; }
        }
        private int _tileOverX = 0;

        public int TileOverY
        {
            get { return _tileOverY; }
            set { _tileOverY = value; }
        }
        private int _tileOverY = 0;

        public int SelectedTileIdx
        {
            get { return _selectedTileIdx; }
            set { _selectedTileIdx = value; }
        }
        private int _selectedTileIdx = 0;

        public TileMap TileMap
        {
            get { return _tileMap; }
            set { _tileMap = value; }
        }
        private TileMap _tileMap = new TileMap();

        public float TileWidthInMeters
        {
            get { return _tileWidthInMeters; }
            set { _tileWidthInMeters = value; }
        }
        private float _tileWidthInMeters = 0.5f;

        public float TileHeightInMeters
        {
            get { return _tileHeightInMeters; }
            set { _tileHeightInMeters = value; }
        }
        private float _tileHeightInMeters = 0.5f;

        public int RightClickTileIdx
        {
            get { return _rightClickTileIdx; }
            set { _rightClickTileIdx = value; }
        }
        private int _rightClickTileIdx = -1;

        #endregion

        #region Methods

        public frmRPGMapEditor()
        {
            InitializeComponent();
        }

        private void toolFile_Exit_Click(object sender, EventArgs e)
        {
            // is the current map saved?
            // ...

            this.Close();
        }

        private void frmRPGMapEditor_Load(object sender, EventArgs e)
        {
            this.textWidthInTiles.Text = this.WidthInTiles.ToString();
            this.textHeightInTiles.Text = this.HeightInTiles.ToString();

            this.textTileWidthInMeters.Text = this.TileWidthInMeters.ToString();
            this.textTileHeightInMeters.Text = this.TileHeightInMeters.ToString();

            this.InitMap();
        }

        private void InitMap()
        {
            if (this.ctrlTileSet1.TileHeight <= 0 ||
                   this.ctrlTileSet1.TileWidth <= 0)
            {
                MessageBox.Show("Please enter a tilewidth and/or a tileheight.");
                return;
            }

            if (this.pictureMap.Image != null)
            {
                this.pictureMap.Image.Dispose();
                this.pictureMap.Image = null;

                this.Map.Dispose();
                this.Map = null;
            }

            if (this.Map == null)
            {
                this.Map = new Bitmap(this.WidthInTiles * this.ctrlTileSet1.TileWidth,
                    this.HeightInTiles * this.ctrlTileSet1.TileHeight,
                    System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
            }
            else
            {
                int newWidth = this.WidthInTiles * this.ctrlTileSet1.TileWidth;
                int newHeight = this.HeightInTiles * this.ctrlTileSet1.TileHeight;

                Bitmap newBmp = new Bitmap(newWidth, newHeight, PixelFormat.Format16bppRgb565);
                Graphics g = Graphics.FromImage(this.Map);

                g.DrawImage(newBmp, 0, 0);
                g.Save();

                this.Map.Dispose();
                this.Map = newBmp;
            }

            this.TileMap.Create(this.WidthInTiles, this.HeightInTiles, this.ctrlTileSet1.TileWidth,
                this.ctrlTileSet1.TileHeight);
        }

        private void RefreshMap()
        {
            if (this.Map.Width > this.pictureMap.Width)
            {
                this.scrollhMap.Minimum = this.pictureMap.Width;
                this.scrollhMap.Maximum = this.Map.Width;
                this.scrollhMap.Enabled = true;
            }
            else
            {
                this.scrollhMap.Enabled = false;
            }

            if (this.Map.Height > this.pictureMap.Height)
            {
                this.scrollvMap.Minimum = this.pictureMap.Height;
                this.scrollvMap.Maximum = this.Map.Height;
                this.scrollvMap.Enabled = true;
            }
            else
            {
                this.scrollvMap.Enabled = false;
            }

            this.UpdateMapLines();
        }

        #endregion

        private void textWidthInTiles_Leave(object sender, EventArgs e)
        {
            if (int.TryParse(this.textWidthInTiles.Text, out this._widthInTiles))
            {
                this.InitMap();
                this.RefreshMap();
            }
        }

        private void textHeightInTiles_Leave(object sender, EventArgs e)
        {
            if (int.TryParse(this.textHeightInTiles.Text, out this._heightInTiles))
            {
                this.InitMap();
                this.RefreshMap();
            }
        }

        private void UpdateMapLines()
        {
            if (this.WidthInTiles <= 0 || this.HeightInTiles <= 0)
                return;

            Rectangle sourceRect = new Rectangle();
            sourceRect.X = this.OffsetX;
            sourceRect.Y = this.OffsetY;
            sourceRect.Width = (this.Map.Width > this.pictureMap.Width) ?
                this.pictureMap.Width : this.Map.Width;
            sourceRect.Height = (this.Map.Height > this.pictureMap.Height) ?
                this.pictureMap.Height : this.Map.Height;

            Bitmap bmp = this.Map.Clone(sourceRect, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);

            Graphics g = Graphics.FromImage(bmp);

            Pen pen = new Pen(new SolidBrush(Color.Yellow));

            for (int i = 0; i < this.WidthInTiles; i++)
            {
                int x = i * this.ctrlTileSet1.TileWidth;
                x -= this.OffsetX;

                Point p1 = new Point(x, 0);
                Point p2 = new Point(x, this.Map.Height - 1);

                g.DrawLine(pen, p1, p2);
            }

            for (int i = 0; i < this.HeightInTiles; i++)
            {
                int y = i * this.ctrlTileSet1.TileHeight;
                y -= this.OffsetY;

                Point p1 = new Point(0, y);
                Point p2 = new Point(this.Map.Width - 1, y);

                g.DrawLine(pen, p1, p2);
            }

            if (this.MouseOverTileIdx > -1)
            {
                int tileWidth = this.Map.Width / this.ctrlTileSet1.TileWidth;

                this.TileOverX = this.MouseOverTileIdx % tileWidth;
                this.TileOverY = this.MouseOverTileIdx / tileWidth;

                int x = this.TileOverX * this.ctrlTileSet1.TileWidth;
                int y = this.TileOverY * this.ctrlTileSet1.TileHeight;

                if (this.OffsetX > 0)
                    x -= this.OffsetX % this.ctrlTileSet1.TileWidth;
                if (this.OffsetY > 0)
                    y -= this.OffsetY % this.ctrlTileSet1.TileHeight;

                Point p1 = new Point(x, y);
                Point p2 = new Point(x + this.ctrlTileSet1.TileWidth, y);
                Point p3 = new Point(x + this.ctrlTileSet1.TileWidth, y + this.ctrlTileSet1.TileHeight);
                Point p4 = new Point(x, y + this.ctrlTileSet1.TileHeight);

                Pen redPen = new Pen(new SolidBrush(Color.Red));

                g.DrawLine(redPen, p1, p2);
                g.DrawLine(redPen, p2, p3);
                g.DrawLine(redPen, p3, p4);
                g.DrawLine(redPen, p4, p1);

                this.statusStrip_CurrentTile.Text = String.Format("Current Tile X: {0} Y: {1}",
                    this.TileOverX, this.TileOverY);
            }
            else
            {
                this.statusStrip_CurrentTile.Text = "Currently Over No Tiles";
            }

            for (int x = 0; x < this.TileMap.MapWidthInTiles; x++)
            {
                for (int y = 0; y < this.TileMap.MapHeightInTiles; y++)
                {
                    int idx = x + (y * this.TileMap.MapWidthInTiles);

                    int tileIdx = this.TileMap.IndexArray[idx];
                    if (tileIdx < 0)
                        continue;

                    if (this.TileMap.TileProperties[idx].IsCollidable)
                    {
                        int tx = x * this.ctrlTileSet1.TileWidth;
                        int ty = y * this.ctrlTileSet1.TileHeight;

                        Point p1 = new Point(tx, ty);
                        Point p2 = new Point(tx + this.ctrlTileSet1.TileWidth, ty);
                        Point p3 = new Point(tx + this.ctrlTileSet1.TileWidth, ty + this.ctrlTileSet1.TileHeight);
                        Point p4 = new Point(tx, ty + this.ctrlTileSet1.TileHeight);

                        Pen redPen = new Pen(new SolidBrush(Color.Blue));

                        g.DrawLine(redPen, p1, p2);
                        g.DrawLine(redPen, p2, p3);
                        g.DrawLine(redPen, p3, p4);
                        g.DrawLine(redPen, p4, p1);
                    }
                }
            }

            g.Save();

            this.pictureMap.Image = bmp;
        }

        private void scrollhMap_Scroll(object sender, ScrollEventArgs e)
        {
            this.OffsetX += e.NewValue - e.OldValue;
            this.UpdateMapLines();
        }

        private void scrollvMap_Scroll(object sender, ScrollEventArgs e)
        {
            this.OffsetY += e.NewValue - e.OldValue;
            this.UpdateMapLines();
        }

        private void pictureMap_MouseLeave(object sender, EventArgs e)
        {
            this.MouseOverTileIdx = -1;
        }

        private void pictureMap_MouseMove(object sender, MouseEventArgs e)
        {
            this.MouseOverTileIdx = this.GetTileIdx(e.X, e.Y);
            if (MouseOverTileIdx > -1)
                this.UpdateMapLines();
        }

        private int GetTileIdx(int x, int y)
        {
            if (this.Map == null ||
                this.ctrlTileSet1.TileWidth == 0 ||
                this.ctrlTileSet1.TileHeight == 0)
                return -1;

            x = x == 0 ? 1 : x;
            y = y == 0 ? 1 : y;

            x += this.OffsetX % this.ctrlTileSet1.TileWidth;
            y += this.OffsetY % this.ctrlTileSet1.TileHeight;

            float fx = x / this.ctrlTileSet1.TileWidth;

            float fy = y / this.ctrlTileSet1.TileHeight;

            int tileWidth = this.Map.Width / this.ctrlTileSet1.TileWidth;

            int idx = Convert.ToInt32(fx + (fy * tileWidth));

            return idx;
        }

        private void pictureMap_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (this.ctrlTileSet1.TileMap == null ||
                    this.ctrlTileSet1.SelectedTileIdx == -1 ||
                    this.ctrlTileSet1.SelectedTileSourceRect == new Rectangle(0,0,0,0))
                {
                    MessageBox.Show("Please select a tilemap and a tile.");
                    return;
                }

                if (this.MouseOverTileIdx == -1)
                    return;

                this.SelectedTileIdx = this.MouseOverTileIdx;

                Bitmap bmp = this.ctrlTileSet1.TileMap.Clone(this.ctrlTileSet1.SelectedTileSourceRect,
                    PixelFormat.Format16bppRgb565);

                int tileWidth = this.Map.Width / this.ctrlTileSet1.TileWidth;

                int x = this.MouseOverTileIdx % tileWidth;
                int y = this.MouseOverTileIdx / tileWidth;

                x *= this.ctrlTileSet1.TileWidth;
                y *= this.ctrlTileSet1.TileHeight;

                x += this.OffsetX;
                y += this.OffsetY;

                if (this.OffsetX > 0)
                    x -= this.OffsetX % this.ctrlTileSet1.TileWidth;
                if (this.OffsetY > 0)
                    y -= this.OffsetY % this.ctrlTileSet1.TileHeight;

                Rectangle destRect = new Rectangle(x, y, this.ctrlTileSet1.TileWidth, this.ctrlTileSet1.TileHeight);

                Graphics g = Graphics.FromImage(this.Map);
                g.DrawImage(bmp, destRect);

                this.UpdateMapLines();

                Microsoft.Xna.Framework.Rectangle xnaRect = new Microsoft.Xna.Framework.Rectangle(
                    this.ctrlTileSet1.SelectedTileSourceRect.X,
                    this.ctrlTileSet1.SelectedTileSourceRect.Y,
                    this.ctrlTileSet1.SelectedTileSourceRect.Width,
                    this.ctrlTileSet1.SelectedTileSourceRect.Height);
                Tile tile = new Tile();
                tile.SourceRect = xnaRect;
                tile.TileSetIdx = this.ctrlTileSet1.CurrentTileSetIdx;
                TileAnimation anim = new TileAnimation();
                anim.Tiles.Add(tile);

                if (!this.TileMap.Tiles.Contains(anim))
                    this.TileMap.Tiles.Add(anim);

                int index = this.TileMap.Tiles.IndexOf(anim);
                this.TileMap.IndexArray[this.SelectedTileIdx] = index;

                // insert tile and assosiated data into tileset data struct here?
                // also, when right clicking, have the option to attach the script events to the selcted tile,
                // as we know what index it is we can do this :)
            }
            else if (e.Button == MouseButtons.Right)
            {
                this.ManageTileProperties();
            }
        }

        private void ManageTileProperties()
        {
            this.RightClickTileIdx = this.MouseOverTileIdx;

            frmTileProperties frm = new frmTileProperties(this);
            if (frm.ShowDialog(this) == DialogResult.OK)
            {
                int idx = this.TileMap.IndexArray[this.RightClickTileIdx];
                if (idx < 0)
                    return;

                this.TileMap.TileProperties[this.RightClickTileIdx].IsCollidable = frm.IsCollidable;
            }
        }

        private void toolFile_Save_Image_Click(object sender, EventArgs e)
        {
            DialogResult res = this.saveMapAsImage.ShowDialog();
            if (res == DialogResult.Cancel)
                return;

            string fname = this.saveMapAsImage.FileName;

            FileStream fp = File.Open(fname, FileMode.Create, FileAccess.ReadWrite);

            string ext = Path.GetExtension(this.saveMapAsImage.FileName);
            ext = ext.ToLower().Trim();

            if (ext == ".png")
                this.Map.Save(fp, ImageFormat.Png);
            else if (ext == ".bmp")
                this.Map.Save(fp, ImageFormat.Bmp);
            else if (ext == ".gif")
                this.Map.Save(fp, ImageFormat.Gif);
            else if (ext == ".jpg")
                this.Map.Save(fp, ImageFormat.Jpeg);
            else
                throw new DSSException(String.Format("'{0}' is an unknown tilemap image save as format.", ext));
        }

        private void toolFile_Save_TileMap_Click(object sender, EventArgs e)
        {
            if (!float.TryParse(this.textTileWidthInMeters.Text, out this._tileWidthInMeters))
            {
                MessageBox.Show("Please enter a valid tile width in meters.");
                return;
            }

            if (!float.TryParse(this.textTileHeightInMeters.Text, out this._tileHeightInMeters))
            {
                MessageBox.Show("Please enter a valid tile height in meters.");
                return;
            }

            this.TileMap.DesiredTileWidthInMeters = this.TileWidthInMeters;
            this.TileMap.DesiredTileHeightInMeters = this.TileHeightInMeters;

            DialogResult res = this.saveMap.ShowDialog();
            if (res == DialogResult.Cancel)
                return;

            this.TileMap.Save(this.saveMap.FileName, this.ctrlTileSet1.MyTileSetManager);
        }

        private void textTileWidthInMeters_MouseLeave(object sender, EventArgs e)
        {

        }

        private void ctrlTileSet1_Load(object sender, EventArgs e)
        {

        }

        private void contextMenuTile_Properties_Click(object sender, EventArgs e)
        {
            if (this.ctrlTileSet1.TileMap == null ||
                this.ctrlTileSet1.SelectedTileIdx == -1 ||
                this.ctrlTileSet1.SelectedTileSourceRect == new Rectangle(0, 0, 0, 0))
            {
                MessageBox.Show("Please select a tilemap and a tile.");
                return;
            }

            if (this.MouseOverTileIdx == -1)
                return;

            this.SelectedTileIdx = this.MouseOverTileIdx;

            frmTileProperties frm = new frmTileProperties(this);
            if (frm.ShowDialog(this) == DialogResult.OK)
                MessageBox.Show("yesh");
        }
    }
}
